import CryptoJS from 'crypto-js'

export interface TTSConfig {
  appId: string
  apiKey: string
  apiSecret: string
}

export interface TTSOptions {
  voice?: string // 发音人
  speed?: number // 语速 0-100
  volume?: number // 音量 0-100
  pitch?: number // 音调 0-100
}

export class XunfeiTTS {
  private config: TTSConfig
  private ws: WebSocket | null = null
  private audioChunks: ArrayBuffer[] = []

  constructor(config: TTSConfig) {
    this.config = config
  }

  private getWebSocketUrl(): string {
    const host = 'tts-api.xfyun.cn'
    const path = '/v2/tts'
    const date = new Date().toUTCString()
    
    const signatureOrigin = `host: ${host}\ndate: ${date}\nGET ${path} HTTP/1.1`
    const signature = CryptoJS.HmacSHA256(signatureOrigin, this.config.apiSecret).toString(CryptoJS.enc.Base64)
    
    const authorization = `hmac username="${this.config.apiKey}", algorithm="hmac-sha256", headers="host date request-line", signature="${signature}"`
    const authorizationBase64 = btoa(authorization)
    
    return `wss://${host}${path}?authorization=${authorizationBase64}&date=${encodeURIComponent(date)}&host=${host}`
  }

  async synthesize(
    text: string, 
    options: TTSOptions = {},
    onProgress?: (chunk: ArrayBuffer) => void
  ): Promise<ArrayBuffer> {
    return new Promise((resolve, reject) => {
      this.audioChunks = []
      
      this.ws = new WebSocket(this.getWebSocketUrl())
      
      this.ws.onopen = () => {
        const params = {
          common: {
            app_id: this.config.appId
          },
          business: {
            aue: 'raw',
            auf: 'audio/L16;rate=16000',
            vcn: options.voice || 'xiaoyan',
            speed: options.speed || 50,
            volume: options.volume || 50,
            pitch: options.pitch || 50,
            bgs: 1,
            tte: 'UTF8'
          },
          data: {
            status: 2,
            text: btoa(unescape(encodeURIComponent(text)))
          }
        }
        
        this.ws!.send(JSON.stringify(params))
      }
      
      this.ws.onmessage = (event) => {
        console.log('🔄 收到TTS WebSocket消息:', event.data)
        
        try {
          // 检查消息数据是否为空或无效
          if (!event.data || typeof event.data !== 'string') {
            console.warn('⚠️ 收到无效的WebSocket消息数据')
            reject(new Error('收到无效的WebSocket消息数据'))
            return
          }
          
          const data = JSON.parse(event.data)
          console.log('📥 解析后的TTS数据:', data)
          
          // 验证响应数据结构
          if (!data || typeof data !== 'object') {
            console.error('❌ 无效的响应数据结构')
            reject(new Error('服务器返回无效数据格式'))
            return
          }
          
          if (data.code === 0) {
            if (data.data && data.data.audio) {
              const audioData = this.base64ToArrayBuffer(data.data.audio)
              this.audioChunks.push(audioData)
              
              if (onProgress) {
                onProgress(audioData)
              }
            }
            
            if (data.data && data.data.status === 2) {
              // 合成完成
              const fullAudio = this.mergeArrayBuffers(this.audioChunks)
              resolve(fullAudio)
              this.cleanup()
            }
          } else {
            console.error('❌ TTS API返回错误:', data)
            let errorMsg = data.message || `TTS错误 (code: ${data.code})`
            
            // 针对特定错误码提供更友好的错误信息
            switch (data.code) {
              case 10004:
                errorMsg = '音频格式不支持，请检查TTS配置'
                break
              case 10013:
                errorMsg = '参数错误，请检查TTS API配置'
                break
              case 11200:
                errorMsg = '认证失败，请检查TTS API密钥'
                break
              default:
                errorMsg = data.message || `TTS错误 (code: ${data.code})`
            }
            
            reject(new Error(errorMsg))
            this.cleanup()
          }
        } catch (parseError) {
          console.error('❌ 解析TTS WebSocket消息失败:', parseError, '原始数据:', event.data)
          reject(new Error('解析服务器响应失败，请检查网络连接'))
          this.cleanup()
        }
      }
      
      this.ws.onerror = () => {
        reject(new Error('WebSocket连接错误'))
        this.cleanup()
      }
      
      this.ws.onclose = () => {
        this.cleanup()
      }
    })
  }

  private base64ToArrayBuffer(base64: string): ArrayBuffer {
    const binaryString = atob(base64)
    const bytes = new Uint8Array(binaryString.length)
    
    for (let i = 0; i < binaryString.length; i++) {
      bytes[i] = binaryString.charCodeAt(i)
    }
    
    return bytes.buffer
  }

  private mergeArrayBuffers(buffers: ArrayBuffer[]): ArrayBuffer {
    const totalLength = buffers.reduce((sum, buffer) => sum + buffer.byteLength, 0)
    const result = new Uint8Array(totalLength)
    let offset = 0
    
    for (const buffer of buffers) {
      result.set(new Uint8Array(buffer), offset)
      offset += buffer.byteLength
    }
    
    return result.buffer
  }

  // 将PCM数据转换为可播放的WAV格式
  pcmToWav(pcmData: ArrayBuffer, sampleRate: number = 16000): ArrayBuffer {
    const pcmLength = pcmData.byteLength
    const wavLength = pcmLength + 44
    const buffer = new ArrayBuffer(wavLength)
    const view = new DataView(buffer)
    
    // WAV文件头
    const writeString = (offset: number, string: string) => {
      for (let i = 0; i < string.length; i++) {
        view.setUint8(offset + i, string.charCodeAt(i))
      }
    }
    
    writeString(0, 'RIFF')
    view.setUint32(4, wavLength - 8, true)
    writeString(8, 'WAVE')
    writeString(12, 'fmt ')
    view.setUint32(16, 16, true)
    view.setUint16(20, 1, true)
    view.setUint16(22, 1, true)
    view.setUint32(24, sampleRate, true)
    view.setUint32(28, sampleRate * 2, true)
    view.setUint16(32, 2, true)
    view.setUint16(34, 16, true)
    writeString(36, 'data')
    view.setUint32(40, pcmLength, true)
    
    // 复制PCM数据
    const pcmView = new Uint8Array(pcmData)
    const wavView = new Uint8Array(buffer)
    wavView.set(pcmView, 44)
    
    return buffer
  }

  // 直接播放合成的语音
  async speak(text: string, options: TTSOptions = {}): Promise<void> {
    try {
      console.log('🔊 开始语音合成并播放:', text)
      
      // 合成语音
      const audioBuffer = await this.synthesize(text, options)
      
      // 转换为WAV格式
      const wavBuffer = this.pcmToWav(audioBuffer)
      
      // 创建音频对象并播放
      const audioBlob = new Blob([wavBuffer], { type: 'audio/wav' })
      const audioUrl = URL.createObjectURL(audioBlob)
      const audio = new Audio(audioUrl)
      
      return new Promise((resolve, reject) => {
        audio.onended = () => {
          URL.revokeObjectURL(audioUrl)
          console.log('✅ 语音播放完成')
          resolve()
        }
        
        audio.onerror = (error) => {
          URL.revokeObjectURL(audioUrl)
          console.error('❌ 语音播放失败:', error)
          reject(error)
        }
        
        audio.play().catch(reject)
      })
    } catch (error) {
      console.error('❌ 语音合成失败:', error)
      throw error
    }
  }

  // 停止当前播放
  stop(): void {
    // 清理WebSocket连接
    this.cleanup()
  }

  private cleanup(): void {
    if (this.ws) {
      this.ws.close()
      this.ws = null
    }
    this.audioChunks = []
  }
}

export default XunfeiTTS